
`timescale 1 ns / 1 ps

    module MiniMAC_1Ge_AXI_v1_0_S00_AXIS #
    (
        // Users to add parameters here

        // User parameters ends
        // Do not modify the parameters beyond this line

        // AXI4Stream sink: Data Width
        parameter integer C_S_AXIS_TDATA_WIDTH  = 32
    )
    (
        // Users to add ports here
        
        input  wire    int_rst  ,
        input  wire    int_clk  ,
        input  wire    gen_en   ,
        output [31: 0] int_data ,
        output         int_valid,
        output         int_sop  ,
        output         int_eop  ,
        output [ 1: 0] int_mod  ,

        // User ports ends
        // Do not modify the ports beyond this line

        // AXI4Stream sink: Clock
        input wire  S_AXIS_ACLK,
        // AXI4Stream sink: Reset
        input wire  S_AXIS_ARESETN,
        // Ready to accept data in
        output wire  S_AXIS_TREADY,
        // Data in
        input wire [C_S_AXIS_TDATA_WIDTH-1 : 0] S_AXIS_TDATA,
        // Byte qualifier
        input wire [(C_S_AXIS_TDATA_WIDTH/8)-1 : 0] S_AXIS_TKEEP,
        // Indicates boundary of last packet
        input wire  S_AXIS_TLAST,
        // Data is in valid
        input wire  S_AXIS_TVALID
    );
//  // function called clogb2 that returns an integer which has the 
//  // value of the ceiling of the log base 2.
//  function integer clogb2 (input integer bit_depth);
//    begin
//      for(clogb2=0; bit_depth>0; clogb2=clogb2+1)
//        bit_depth = bit_depth >> 1;
//    end
//  endfunction

//  // Total number of input data.
//  localparam NUMBER_OF_INPUT_WORDS  = 8;
//  // bit_num gives the minimum number of bits needed to address 'NUMBER_OF_INPUT_WORDS' size of FIFO.
//  localparam bit_num  = clogb2(NUMBER_OF_INPUT_WORDS-1);
//  // Define the states of state machine
//  // The control state machine oversees the writing of input streaming data to the FIFO,
//  // and outputs the streaming data from the FIFO
//  parameter [1:0] IDLE = 1'b0,        // This is the initial/idle state 

//                  WRITE_FIFO  = 1'b1; // In this state FIFO is written with the
//                                      // input stream data S_AXIS_TDATA 
//  wire    axis_tready;
//  // State variable
//  reg mst_exec_state;  
//  // FIFO implementation signals
//  genvar byte_index;     
//  // FIFO write enable
//  wire fifo_wren;
//  // FIFO full flag
//  reg fifo_full_flag;
//  // FIFO write pointer
//  reg [bit_num-1:0] write_pointer;
//  // sink has accepted all the streaming data and stored in FIFO
//    reg writes_done;
//  // I/O Connections assignments

//  assign S_AXIS_TREADY    = axis_tready;
//  // Control state machine implementation
//  always @(posedge S_AXIS_ACLK) 
//  begin  
//    if (!S_AXIS_ARESETN) 
//    // Synchronous reset (active low)
//      begin
//        mst_exec_state <= IDLE;
//      end  
//    else
//      case (mst_exec_state)
//        IDLE: 
//          // The sink starts accepting tdata when 
//          // there tvalid is asserted to mark the
//          // presence of valid streaming data 
//            if (S_AXIS_TVALID)
//              begin
//                mst_exec_state <= WRITE_FIFO;
//              end
//            else
//              begin
//                mst_exec_state <= IDLE;
//              end
//        WRITE_FIFO: 
//          // When the sink has accepted all the streaming input data,
//          // the interface swiches functionality to a streaming master
//          if (writes_done)
//            begin
//              mst_exec_state <= IDLE;
//            end
//          else
//            begin
//              // The sink accepts and stores tdata 
//              // into FIFO
//              mst_exec_state <= WRITE_FIFO;
//            end

//      endcase
//  end
//  // AXI Streaming Sink 
//  // 
//  // The example design sink is always ready to accept the S_AXIS_TDATA  until
//  // the FIFO is not filled with NUMBER_OF_INPUT_WORDS number of input words.
//  assign axis_tready = ((mst_exec_state == WRITE_FIFO) && (write_pointer <= NUMBER_OF_INPUT_WORDS-1));

//  always@(posedge S_AXIS_ACLK)
//  begin
//    if(!S_AXIS_ARESETN)
//      begin
//        write_pointer <= 0;
//        writes_done <= 1'b0;
//      end  
//    else
//      if (write_pointer <= NUMBER_OF_INPUT_WORDS-1)
//        begin
//          if (fifo_wren)
//            begin
//              // write pointer is incremented after every write to the FIFO
//              // when FIFO write signal is enabled.
//              write_pointer <= write_pointer + 1;
//              writes_done <= 1'b0;
//            end
//            if ((write_pointer == NUMBER_OF_INPUT_WORDS-1)|| S_AXIS_TLAST)
//              begin
//                // reads_done is asserted when NUMBER_OF_INPUT_WORDS numbers of streaming data 
//                // has been written to the FIFO which is also marked by S_AXIS_TLAST(kept for optional usage).
//                writes_done <= 1'b1;
//              end
//        end  
//  end

//  // FIFO write enable generation
//  assign fifo_wren = S_AXIS_TVALID && axis_tready;

//  // FIFO Implementation
//  generate 
//    for(byte_index=0; byte_index<= (C_S_AXIS_TDATA_WIDTH/8-1); byte_index=byte_index+1)
//    begin:FIFO_GEN

//      reg  [(C_S_AXIS_TDATA_WIDTH/4)-1:0] stream_data_fifo [0 : NUMBER_OF_INPUT_WORDS-1];

//      // Streaming input data is stored in FIFO

//      always @( posedge S_AXIS_ACLK )
//      begin
//        if (fifo_wren)// && S_AXIS_TKEEP[byte_index])
//          begin
//            stream_data_fifo[write_pointer] <= S_AXIS_TDATA[(byte_index*8+7) -: 8];
//          end  
//      end  
//    end       
//  endgenerate

    // Add user logic here
    
    //////////////////////////
    // Naive implementation
    //////////////////////////
    
//    assign S_AXIS_TREADY  = 1'b1;
//    assign int_data  = S_AXIS_TDATA[31:0];
//    assign int_valid = S_AXIS_TDATA[36];//S_AXIS_TVALID;
//    assign int_sop   = S_AXIS_TDATA[35];
//    assign int_eop   = S_AXIS_TDATA[34];//S_AXIS_TLAST;
//    assign int_mod   = S_AXIS_TDATA[33:32];//S_AXIS_TKEEP;

    /////////////////////////////////////////////
    // (PART I)
    // Store-Forward Buffering implementation
    /////////////////////////////////////////////

    /////////////////////////////////////////////
    // (CODE BLOCK 1) DC FIFO INSTANTIATION
    /////////////////////////////////////////////
    
    // FIFO_DUALCLOCK_MACRO: Dual Clock First-In, First-Out (FIFO) RAM Buffer
    //                       Artix-7
    // Xilinx HDL Language Template, version 2017.1
    
    /////////////////////////////////////////////////////////////////
    // DATA_WIDTH | FIFO_SIZE | FIFO Depth | RDCOUNT/WRCOUNT Width //
    // ===========|===========|============|=======================//
    //   37-72    |  "36Kb"   |     512    |         9-bit         //
    //   19-36    |  "36Kb"   |    1024    |        10-bit         //
    //   19-36    |  "18Kb"   |     512    |         9-bit         //
    //   10-18    |  "36Kb"   |    2048    |        11-bit         //
    //   10-18    |  "18Kb"   |    1024    |        10-bit         //
    //    5-9     |  "36Kb"   |    4096    |        12-bit         //
    //    5-9     |  "18Kb"   |    2048    |        11-bit         //
    //    1-4     |  "36Kb"   |    8192    |        13-bit         //
    //    1-4     |  "18Kb"   |    4096    |        12-bit         //
    /////////////////////////////////////////////////////////////////
    
    wire                            DATA_FIFO_RST;
    wire                            DATA_FIFO_WRCLK;
    wire                            DATA_FIFO_WREN;
    wire [C_S_AXIS_TDATA_WIDTH-1:0] DATA_FIFO_DI;
    wire                            DATA_FIFO_FULL;
    wire                            DATA_FIFO_RDCLK;
    wire                            DATA_FIFO_RDEN;
    wire [C_S_AXIS_TDATA_WIDTH-1:0] DATA_FIFO_DO;
    wire                            DATA_FIFO_EMPTY;
    FIFO_DUALCLOCK_MACRO  #(
       .ALMOST_EMPTY_OFFSET(9'h080), // Sets the almost empty threshold
       .ALMOST_FULL_OFFSET(9'h080),  // Sets almost full threshold
       .DATA_WIDTH(C_S_AXIS_TDATA_WIDTH),   // Valid values are 1-72 (37-72 only valid when FIFO_SIZE="36Kb")
       .DEVICE("7SERIES"),  // Target device: "7SERIES" 
       .FIFO_SIZE ("36Kb"), // Target BRAM: "18Kb" or "36Kb" 
       .FIRST_WORD_FALL_THROUGH ("FALSE") // Sets the FIFO FWFT to "TRUE" or "FALSE" 
    ) DATA_FIFO (
       .RST  (DATA_FIFO_RST),               // 1-bit input reset
       .WRCLK(DATA_FIFO_WRCLK),             // 1-bit input write clock
       .WREN (DATA_FIFO_WREN),              // 1-bit input write enable
       .DI   (DATA_FIFO_DI),                // Input data, width defined by DATA_WIDTH parameter
       .FULL (DATA_FIFO_FULL),              // 1-bit output full
       .RDCLK(DATA_FIFO_RDCLK),             // 1-bit input read clock
       .RDEN (DATA_FIFO_RDEN),              // 1-bit input read enable
       .DO   (DATA_FIFO_DO),                // Output data, width defined by DATA_WIDTH parameter
       .EMPTY(DATA_FIFO_EMPTY),             // 1-bit output empty
       .ALMOSTEMPTY(           ), // 1-bit output almost empty
       .ALMOSTFULL(          ),   // 1-bit output almost full
       .RDCOUNT(       ),         // Output read count, width determined by FIFO depth
       .RDERR(     ),             // 1-bit output read error
       .WRCOUNT(       ),         // Output write count, width determined by FIFO depth
       .WRERR(      )             // 1-bit output write error
    );

    wire          CTRL_FIFO_RST;
    wire          CTRL_FIFO_WRCLK;
    wire          CTRL_FIFO_WREN;
    wire [16-1:0] CTRL_FIFO_DI;
    wire          CTRL_FIFO_FULL;
    wire          CTRL_FIFO_RDCLK;
    wire          CTRL_FIFO_RDEN;
    wire [16-1:0] CTRL_FIFO_DO;
    wire          CTRL_FIFO_EMPTY;
    FIFO_DUALCLOCK_MACRO  #(
       .ALMOST_EMPTY_OFFSET(9'h080), // Sets the almost empty threshold
       .ALMOST_FULL_OFFSET(9'h080),  // Sets almost full threshold
       .DATA_WIDTH(16),   // Valid values are 1-72 (37-72 only valid when FIFO_SIZE="36Kb")
       .DEVICE("7SERIES"),  // Target device: "7SERIES" 
       .FIFO_SIZE ("18Kb"), // Target BRAM: "18Kb" or "36Kb" 
       .FIRST_WORD_FALL_THROUGH ("FALSE") // Sets the FIFO FWFT to "TRUE" or "FALSE" 
    ) CTRL_FIFO (
       .RST  (CTRL_FIFO_RST),               // 1-bit input reset
       .WRCLK(CTRL_FIFO_WRCLK),             // 1-bit input write clock
       .WREN (CTRL_FIFO_WREN),              // 1-bit input write enable
       .DI   (CTRL_FIFO_DI),                // Input data, width defined by DATA_WIDTH parameter
       .FULL (CTRL_FIFO_FULL),              // 1-bit output full
       .RDCLK(CTRL_FIFO_RDCLK),             // 1-bit input read clock
       .RDEN (CTRL_FIFO_RDEN),              // 1-bit input read enable
       .DO   (CTRL_FIFO_DO),                // Output data, width defined by DATA_WIDTH parameter
       .EMPTY(CTRL_FIFO_EMPTY),             // 1-bit output empty
       .ALMOSTEMPTY(           ), // 1-bit output almost empty
       .ALMOSTFULL(          ),   // 1-bit output almost full
       .RDCOUNT(       ),         // Output read count, width determined by FIFO depth
       .RDERR(     ),             // 1-bit output read error
       .WRCOUNT(       ),         // Output write count, width determined by FIFO depth
       .WRERR(      )             // 1-bit output write error
    );
    
    /////////////////////////////////////////
    // (CODE BLOCK 2) WRITE FIFO LOGIC
    /////////////////////////////////////////
    
    // write DFIFO
    // GENERAL MAPPING
    wire                            dfifo_wren = S_AXIS_TVALID && S_AXIS_TREADY;
    wire [C_S_AXIS_TDATA_WIDTH-1:0] dfifo_din  = S_AXIS_TDATA;
    
    // write CFIFO
    reg S_AXIS_TVALID_trig;
    always @(posedge S_AXIS_ACLK or negedge S_AXIS_ARESETN) begin
        if (!S_AXIS_ARESETN)
            S_AXIS_TVALID_trig <= 1'b0;
        else if (S_AXIS_TVALID && S_AXIS_TREADY && S_AXIS_TLAST)
            S_AXIS_TVALID_trig <= 1'b0;
        else if (S_AXIS_TVALID && S_AXIS_TREADY)
            S_AXIS_TVALID_trig <= 1'b1;
    end
    wire S_AXIS_TVALID_pos = S_AXIS_TVALID && !S_AXIS_TVALID_trig;
    reg          cfifo_din_pbad;
    always @(posedge S_AXIS_ACLK or negedge S_AXIS_ARESETN) begin
        if (!S_AXIS_ARESETN)
            cfifo_din_pbad <= 1'b0;
        else
            if      (S_AXIS_TVALID && S_AXIS_TREADY && DATA_FIFO_FULL)
                cfifo_din_pbad <= 1'b1;  // set
            else if (S_AXIS_TVALID && S_AXIS_TREADY && S_AXIS_TLAST)
                cfifo_din_pbad <= 1'b0;  // clear
    end
    reg  [14-1:0] cfifo_din_pcnt;
    always @(posedge S_AXIS_ACLK or negedge S_AXIS_ARESETN) begin
        if (!S_AXIS_ARESETN)
            cfifo_din_pcnt <= 14'd0;
        else
            if (S_AXIS_TVALID_pos)
                cfifo_din_pcnt <= 14'd4;  // init
            else if (S_AXIS_TVALID && S_AXIS_TREADY && S_AXIS_TLAST)
                case (S_AXIS_TKEEP)
                    4'b0001: cfifo_din_pcnt <= cfifo_din_pcnt + 14'd1;  // last
                    4'b0011: cfifo_din_pcnt <= cfifo_din_pcnt + 14'd2;  // last
                    4'b0111: cfifo_din_pcnt <= cfifo_din_pcnt + 14'd3;  // last
                    4'b1111: cfifo_din_pcnt <= cfifo_din_pcnt + 14'd4;  // last
                endcase
            else if (S_AXIS_TVALID && S_AXIS_TREADY)  // incr
                cfifo_din_pcnt <= cfifo_din_pcnt + 14'd4;
    end
    reg          cfifo_wren_reg;
    always @(posedge S_AXIS_ACLK or negedge S_AXIS_ARESETN) begin
        if (!S_AXIS_ARESETN)
            cfifo_wren_reg <= 1'b0;
        else
            cfifo_wren_reg <= S_AXIS_TVALID && S_AXIS_TREADY && S_AXIS_TLAST;
    end
    // GENERAL MAPPING
    wire          cfifo_wren = cfifo_wren_reg;
    wire [16-1:0] cfifo_din  = {cfifo_din_pbad, 1'd0, cfifo_din_pcnt};  // {1,1,14}

    /////////////////////////////////////////
    // (CODE BLOCK 3) READ FIFO LOGIC
    /////////////////////////////////////////
    
    // fsm input signals
    wire          dfifo_read_done;
    // fsm output signals
    reg  cfifo_rden_reg;
    reg  dfifo_rden_reg;
    // GENERAL MAPPING
    wire dfifo_rden = gen_en && dfifo_rden_reg && !DATA_FIFO_EMPTY;
    wire cfifo_rden = gen_en && cfifo_rden_reg;

    
    // fsm state transition
    reg  [ 7:0] fsm_read_curr_st, fsm_read_next_st, fsm_read_last_st;
    
    parameter [ 7:0]
    FSM_READ_IDLE    =                       0,  // 0
    FSM_READ_CTRL    = FSM_READ_IDLE       + 1,  // 1
    FSM_READ_DATA    = FSM_READ_CTRL       + 1;  // 2
    
    always @(posedge int_clk or posedge int_rst) begin
        if (int_rst) begin
            fsm_read_curr_st <= FSM_READ_IDLE;
            fsm_read_last_st <= FSM_READ_IDLE;
        end
        else if (gen_en) begin
            fsm_read_curr_st <= fsm_read_next_st;
            fsm_read_last_st <= fsm_read_curr_st;
        end
    end
    
    always @(*) begin
        case (fsm_read_curr_st)
            FSM_READ_IDLE:
                if (!CTRL_FIFO_EMPTY)
                    fsm_read_next_st = FSM_READ_CTRL;
                else
                    fsm_read_next_st = FSM_READ_IDLE;
            FSM_READ_CTRL:
                    fsm_read_next_st = FSM_READ_DATA;
            FSM_READ_DATA:
                if (dfifo_read_done)
                    fsm_read_next_st = FSM_READ_IDLE;
                else
                    fsm_read_next_st = FSM_READ_DATA;
            default:
                fsm_read_next_st = FSM_READ_IDLE;
        endcase
    end
    
    // fsm input logic
    wire [13:0] packet_leng = CTRL_FIFO_DO[13:0];
    reg  [16-1:0] dfifo_read_rcnt;
    always @(posedge int_clk or posedge int_rst) begin
        if (int_rst)
            dfifo_read_rcnt <= 14'd0;
        else if (gen_en)
            if (fsm_read_next_st == FSM_READ_IDLE)
                dfifo_read_rcnt <= 14'd0;
            else if (dfifo_rden)
                dfifo_read_rcnt <= dfifo_read_rcnt + 14'd4;
    end
    assign dfifo_read_done = (dfifo_read_rcnt+14'd4 >= packet_leng)? 1'b1:1'b0;    
    
    // fsm output logic
    always @(posedge int_clk or posedge int_rst) begin
        if (int_rst)
            {cfifo_rden_reg, dfifo_rden_reg} <= {1'b0, 1'b0};
        else if (gen_en) begin
            if (fsm_read_next_st == FSM_READ_CTRL)
                cfifo_rden_reg <= 1'b1;
            else
                cfifo_rden_reg <= 1'b0;
                
            if (fsm_read_next_st == FSM_READ_DATA)
                dfifo_rden_reg <= 1'b1;
            else
                dfifo_rden_reg <= 1'b0;
        end
    end
    
    /////////////////////////////////////////
    // (CODE BLOCK 4) FIFO SIGNAL MAPPING
    /////////////////////////////////////////
    
    assign DATA_FIFO_RST   = !S_AXIS_ARESETN;
    assign DATA_FIFO_WRCLK = S_AXIS_ACLK;
    assign DATA_FIFO_WREN  = dfifo_wren && !DATA_FIFO_FULL;
    assign DATA_FIFO_DI    = dfifo_din;
    //assign DATA_FIFO_FULL  = ;
    assign DATA_FIFO_RDCLK = int_clk;
    assign DATA_FIFO_RDEN  = dfifo_rden && !DATA_FIFO_EMPTY;
    //assign DATA_FIFO_DO    = ;
    //assign DATA_FIFO_EMPTY = ;
    
    assign CTRL_FIFO_RST   = !S_AXIS_ARESETN;
    assign CTRL_FIFO_WRCLK = S_AXIS_ACLK;
    assign CTRL_FIFO_WREN  = cfifo_wren && !CTRL_FIFO_FULL;
    assign CTRL_FIFO_DI    = cfifo_din;
    //assign CTRL_FIFO_FULL  = ;
    assign CTRL_FIFO_RDCLK = int_clk;
    assign CTRL_FIFO_RDEN  = cfifo_rden && !CTRL_FIFO_EMPTY;
    //assign CTRL_FIFO_DO    = ;
    //assign CTRL_FIFO_EMPTY = ;
    
    /////////////////////////////////////////
    // (PART II)
    // AXI-S MASTER SIGNAL GENERATION
    /////////////////////////////////////////
    
    /////////////////////////////////////////
    // (CODE BLOCK 1) AXI-S MASTER SIGNAL GENERATION
    /////////////////////////////////////////
    wire axis_tready = !DATA_FIFO_FULL;
    
    /////////////////////////////////////////
    // (CODE BLOCK 2) AXI-S MASTER SIGNAL MAPPING
    /////////////////////////////////////////
    assign S_AXIS_TREADY = axis_tready;
    
    /////////////////////////////////////////
    // (PART III)
    // INTERNAL STREAMING SIGNAL GENERATION
    /////////////////////////////////////////
     
    /////////////////////////////////////////
    // (CODE BLOCK 1) INTERNAL STREAMING SIGNAL GENERATION
    /////////////////////////////////////////
    
    reg int_valid_reg;
    always @(posedge int_clk or posedge int_rst) begin
        if (int_rst)
            int_valid_reg <= 1'b0;
        else if (gen_en)
            int_valid_reg <= dfifo_rden_reg;
    end
    reg int_sop_reg;
    always @(posedge int_clk or posedge int_rst) begin
        if (int_rst)
            int_sop_reg <= 1'b0;
        else if (gen_en)
            int_sop_reg <= (fsm_read_last_st != FSM_READ_DATA) && (fsm_read_curr_st == FSM_READ_DATA);
    end
    reg int_eop_reg;
    always @(posedge int_clk or posedge int_rst) begin
        if (int_rst)
            int_eop_reg <= 1'b0;
        else if (gen_en)
            int_eop_reg <= (fsm_read_curr_st == FSM_READ_DATA) && dfifo_read_done;
    end
    reg [1:0] int_mod_reg;
    always @(posedge int_clk or posedge int_rst) begin
        if (int_rst)
            int_mod_reg <= 2'b00;
        else if (gen_en)
            int_mod_reg <= ((fsm_read_curr_st == FSM_READ_DATA) && dfifo_read_done)? packet_leng[1:0]: 2'b00;
    end
    
    /////////////////////////////////////////
    // (CODE BLOCK 2) INTERNAL STREAMING SIGNAL MAPPING
    /////////////////////////////////////////
    
    assign int_data  = DATA_FIFO_DO;
    assign int_valid = int_valid_reg;
    assign int_sop   = int_sop_reg;
    assign int_eop   = int_eop_reg;
    assign int_mod   = int_mod_reg;
    
    // User logic ends

    endmodule
